package com.yic.module.wms.service.producein;

import cn.hutool.core.util.ObjectUtil;
import com.yic.framework.security.core.util.SecurityFrameworkUtils;
import com.yic.module.mes.api.mo.MoApi;
import com.yic.module.mes.api.mo.dto.MoRespDTO;
import com.yic.module.system.api.user.AdminUserApi;
import com.yic.module.system.api.user.dto.AdminUserRespDTO;
import com.yic.module.system.enums.common.ApproveEnum;
import com.yic.module.system.enums.dict.WmsInventoryBusinessTypeEnum;
import com.yic.module.wms.dal.dataobject.itemwarehouse.ItemWarehouseDO;
import com.yic.module.wms.service.inventory.InventoryService;
import com.yic.module.wms.service.itemwarehouse.ItemWarehouseService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import com.yic.module.wms.controller.admin.producein.vo.*;
import com.yic.module.wms.dal.dataobject.producein.ProduceInDO;
import com.yic.framework.common.pojo.PageResult;

import com.yic.module.wms.convert.producein.ProduceInConvert;
import com.yic.module.wms.dal.mysql.producein.ProduceInMapper;

import static com.yic.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yic.module.wms.enums.ErrorCodeConstants.*;

/**
 * 生产入库单 Service 实现类
 *
 * @author 系统管理员
 */
@Service
@Validated
public class ProduceInServiceImpl implements ProduceInService {

    @Resource
    private ProduceInMapper produceInMapper;
    @Resource
    private InventoryService inventoryService;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MoApi moApi;

    @Override
    public Long createProduceIn(ProduceInCreateReqVO createReqVO) {
        validateCreateOrUpdate(null, createReqVO.getCode(), createReqVO.getMoId(), createReqVO.getInAmount());
        //查询用户信息
        AdminUserRespDTO user = adminUserApi.getUser(SecurityFrameworkUtils.getLoginUserId());
        //组装数据
        createReqVO.setUserId(user.getId());
        createReqVO.setDeptId(user.getDeptId());
        // 插入
        ProduceInDO produceIn = ProduceInConvert.INSTANCE.convert(createReqVO);
        produceInMapper.insert(produceIn);
        // 返回
        return produceIn.getId();
    }

    @Override
    public void updateProduceIn(ProduceInUpdateReqVO updateReqVO) {
        // 校验存在
        validateCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode(), updateReqVO.getMoId(), updateReqVO.getInAmount());
        // 更新
        ProduceInDO updateObj = ProduceInConvert.INSTANCE.convert(updateReqVO);
        produceInMapper.updateById(updateObj);
    }

    @Override
    public void deleteProduceIn(Long id) {
        // 校验存在
        validateProduceInExists(id);
        // 删除
        produceInMapper.deleteById(id);
    }

    private void validateCreateOrUpdate(Long id, String code, Long moId, BigDecimal amount){
        validateCodeExists(id, code);
        validateMO(moId, amount);
    }

    private void validateMO(Long moId, BigDecimal amount) {
        MoRespDTO mo = moApi.getMo(moId);
        if (mo.getProduceAmount().subtract(mo.getInAmount()).compareTo(amount) < 0) {
            throw exception(PRODUCE_IN_GT_PRODUCE);
        }
    }

    private void validateCodeExists(Long id, String code) {
        ProduceInDO produceInDO = produceInMapper.selectByCode(code);
        if (ObjectUtil.isEmpty(produceInDO)) {
            return;
        }
        if (id == null) {
            throw exception(PRODUCE_IN_CODE_EXISTS);
        }
        if (!id.equals(produceInDO.getId())) {
            throw exception(PRODUCE_IN_CODE_EXISTS);
        }
    }

    private ProduceInDO validateProduceInExists(Long id) {
        ProduceInDO produceInDO = produceInMapper.selectById(id);
        if (produceInDO == null) {
            throw exception(PRODUCE_IN_NOT_EXISTS);
        }
        return produceInDO;
    }

    @Override
    public ProduceInDO getProduceIn(Long id) {
        return produceInMapper.selectById(id);
    }

    @Override
    public List<ProduceInDO> getProduceInList(Collection<Long> ids) {
        return produceInMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<ProduceInDO> getProduceInPage(ProduceInPageReqVO pageReqVO) {
        return produceInMapper.selectPage(pageReqVO);
    }

    @Override
    public List<ProduceInDO> getProduceInList(ProduceInExportReqVO exportReqVO) {
        return produceInMapper.selectList(exportReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(Long id) {
        ProduceInDO produceInDO = validateProduceInExists(id);
        if (!ApproveEnum.WAIT.getApprove().equals(produceInDO.getStatus())) {
            throw exception(PRODUCE_IN_NOT_CHANGE);
        }

        produceInDO.setApproveTime(LocalDateTime.now());
        produceInDO.setApproveUserId(SecurityFrameworkUtils.getLoginUserId());
        produceInDO.setStatus(ApproveEnum.PASS.getApprove());
        produceInMapper.updateById(produceInDO);
        inventoryService.increaseInventory(produceInDO.getFactoryId(), produceInDO.getWarehouseId(), produceInDO.getAreaId(),
                produceInDO.getLocationId(), produceInDO.getItemId(), produceInDO.getItemWarehouseId(), produceInDO.getInAmount(),
                produceInDO.getBatchNo(), null, produceInDO.getRemark(), produceInDO.getInTime(), WmsInventoryBusinessTypeEnum.PRODUCE.getInventoryBusinessType(),
                produceInDO.getId(), produceInDO.getCode());
        moApi.changeInAmount(produceInDO.getMoId(), produceInDO.getInAmount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Long id) {
        ProduceInDO produceInDO = validateProduceInExists(id);
        if (!ApproveEnum.PASS.getApprove().equals(produceInDO.getStatus())) {
            throw exception(PRODUCE_IN_NOT_CHANGE);
        }
        produceInDO.setStatus(ApproveEnum.WAIT.getApprove());
        produceInMapper.updateById(produceInDO);
        inventoryService.cancelIncreaseInventory(produceInDO.getWarehouseId(), produceInDO.getItemWarehouseId(), produceInDO.getInAmount(),
                WmsInventoryBusinessTypeEnum.PRODUCE.getInventoryBusinessType(), id);
        moApi.changeInAmount(produceInDO.getMoId(), produceInDO.getInAmount().negate());
    }

}
